﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using Microsoft.Win32.SafeHandles;
using JasLib.Extensions;
using JasLib.Interop.Win32;

namespace JasLib.Interop.Win32
{
    public static class Win32Utilities
    {
        /***************************************************************************/
        public static Win32ErrorCode BuildRunDll32Parameters(
            string strModulePath,
            string strEntryPointName,
            string strExtraParameters,
            out string strFinal)
        {
            strFinal = null;

            string strFullModulePath = KERNEL32.GetShortPathName(strModulePath);
            if (strFullModulePath == null)
                return KERNEL32.GetLastError();

            if (strFullModulePath.IndexOfAny(new char[] { ' ', ',', '\"' }, 0) != -1)
                return Win32ErrorCode.ERROR_FILE_INVALID;

            strFinal = string.Format("{0},{1} {2}", strFullModulePath, strEntryPointName, strExtraParameters);
            return Win32ErrorCode.ERROR_SUCCESS;
        }

        /***************************************************************************/
        /// <summary>
        /// WaitHandle is strangely abstract, so we only needed to inherit to use it.
        /// </summary>
        private class UnspecializedWaitHandle : WaitHandle
        {
        }

        public static WaitHandle CreateWaitHandle(UINT_PTR uiRawHandleValue)
        {
            return new UnspecializedWaitHandle { SafeWaitHandle = new SafeWaitHandle(uiRawHandleValue, true) };
        }

        /***************************************************************************/
        public static void DeleteServices(params string[] serviceNames)
        {
            DeleteServices((IEnumerable<string>)serviceNames);
            return;
        }

        /***************************************************************************/
        public static void DeleteServices(IEnumerable<string> serviceNames)
        {
            using (var hServiceControlManager = ADVAPI32.OpenSCManager(null, null, ServiceManagerAccessRights.DELETE))
            {
                if (hServiceControlManager.IsInvalid)
                    throw new Win32Exception("Unable to open the service controller.");

                foreach (var strServiceName in serviceNames)
                {
                    using (var hService = ADVAPI32.OpenService(hServiceControlManager, strServiceName, ServiceAccessRights.DELETE))
                    {
                        if (hService.IsInvalid)
                            throw new Win32Exception("Unable to open the service to perform deletion.");

                        if (!ADVAPI32.DeleteService(hService))
                            throw new Win32Exception("Unable to delete the service.");
                    }
                }
            }

            return;
        }

        /***************************************************************************/
        /// <summary>
        /// http://support.microsoft.com/kb/164787
        /// </summary>
        public static Win32ErrorCode LaunchRunDll32For32BitModule(
            string strModulePath,
            string strEntryPointName,
            string strParameters)
        {
            Win32ErrorCode eErrorCode = Win32ErrorCode.ERROR_SUCCESS;

            string strFinalParameters;
            eErrorCode = BuildRunDll32Parameters(strModulePath, strEntryPointName, strParameters, out strFinalParameters);
            if (eErrorCode != Win32ErrorCode.ERROR_SUCCESS)
                return eErrorCode;

            /// TODO: Not finished.

            return eErrorCode;
        }

        /***************************************************************************/
        public static Win32ErrorCode LaunchRunDll32For64BitModule(
            string strModulePath,
            string strEntryPointName,
            string strParameters)
        {
            Win32ErrorCode eErrorCode = Win32ErrorCode.ERROR_SUCCESS;

            string strFinalParameters;
            eErrorCode = BuildRunDll32Parameters(strModulePath, strEntryPointName, strParameters, out strFinalParameters);
            if (eErrorCode != Win32ErrorCode.ERROR_SUCCESS)
                return eErrorCode;

            /// TODO: Not finished.

            return eErrorCode;
        }

        /***************************************************************************/
        /// <summary>
        /// Converted from VB written by Dustin Aleksiuk.
        /// http://www.codinghorror.com/blog/archives/000264.html
        /// </summary>
        public static DateTime RetrieveLinkerTimestampFromPEHeader(string strFilePath)
        {
            const int iPeHeaderOffset = 60;
            const int iLinkerTimestampOffset = 8;

            byte[] b = new byte[2048];

            using (Stream s = new FileStream(strFilePath, FileMode.Open, FileAccess.Read))
            {
                s.Read(b, 0, 2048);
            }

            int i = BitConverter.ToInt32(b, iPeHeaderOffset);
            int iSecondsSince1970 = BitConverter.ToInt32(b, i + iLinkerTimestampOffset);

            DateTime dt = new DateTime(1970, 1, 1, 0, 0, 0);
            dt = dt.AddSeconds(iSecondsSince1970);
            //dt = dt.AddHours(TimeZone.CurrentTimeZone.GetUtcOffset(dt).Hours);
            return dt;
        }

        /***************************************************************************/
        public static void SetServiceDescription(SC_HANDLE hService, string strDescription)
        {
            SERVICE_DESCRIPTION newDescription = new SERVICE_DESCRIPTION
            {
                lpDescription = strDescription,
            };

            if (!ADVAPI32.ChangeServiceConfig2(hService, ServiceConfigInfoLevel.SERVICE_CONFIG_DESCRIPTION, ref newDescription))
                throw new Win32Exception("Failed call to ChangeServiceConfig2(SERVICE_CONFIG_DESCRIPTION).");
            return;
        }
    }
}
